package cn.com.blueInfo.core.error;

import cn.com.blueInfo.utils.error.BusinessException;
import cn.com.blueInfo.utils.result.ResultInfo;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;

@Log4j2
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理自定义业务异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BusinessException.class)
    public ResultInfo<Object> handleBusinessException(BusinessException e) {
        log.error(e.getMessage(), e);
        return ResultInfo.builder()
                .code(e.getCode())
                .result(false)
                .message(e.getMessage())
                .error(e.getClass().getSimpleName())
                .build();
    }

    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(IllegalArgumentException.class)
    public ResultInfo<Object> handleIllegalArgumentException(IllegalArgumentException e) {
        log.error(e.getMessage(), e);
        return ResultInfo.builder()
                .code(401)
                .result(false)
                .message(e.getMessage())
                .error(e.getClass().getSimpleName())
                .build();
    }

    /**
     * 处理参数校验异常（@Valid注解）
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultInfo<Map<String, String>> handleValidationException(MethodArgumentNotValidException e) {
        log.error("参数校验失败", e);
        Map<String, String> errors = new HashMap<>();
        e.getBindingResult().getFieldErrors()
                .forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));

        return ResultInfo.<Map<String, String>>builder()
                .code(400)
                .result(false)
                .message("参数校验失败")
                .data(errors)
                .error("ValidationError")
                .build();
    }

    /**
     * 处理路径参数校验异常（@PathVariable和@RequestParam）
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public ResultInfo<Map<String, String>> handleConstraintViolationException(ConstraintViolationException e) {
        log.error("参数校验失败", e);
        Map<String, String> errors = new HashMap<>();
        e.getConstraintViolations()
                .forEach(violation -> {
                    String propertyPath = violation.getPropertyPath().toString();
                    String message = violation.getMessage();
                    errors.put(propertyPath, message);
                });

        return ResultInfo.<Map<String, String>>builder()
                .code(400)
                .result(false)
                .message("参数校验失败")
                .data(errors)
                .error("ValidationError")
                .build();
    }

    /**
     * 处理空指针异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(NullPointerException.class)
    public ResultInfo<Object> handleNullPointerException(NullPointerException e) {
        log.error("系统内部错误，请联系管理员", e);
        return ResultInfo.builder()
                .code(500)
                .result(false)
                .message("系统内部错误，请联系管理员")
                .error(e.getClass().getSimpleName())
                .build();
    }

    /**
     * 控制器认证异常捕获与返回前端
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BindException.class)
    public ResultInfo<JSONObject> handleBindException(BindException e) {
        StringBuffer errorMessage = new StringBuffer();
        JSONObject errResult = new JSONObject();
        e.getFieldErrors().forEach(field -> {
            errResult.put(field.getField(), field.getDefaultMessage());
            errorMessage.append(field.getDefaultMessage()).append(", ");
        });
        errorMessage.setLength(errorMessage.length() - 2);
        log.error("字段校验失败", e);
        return ResultInfo.<JSONObject>builder()
                .code(500)
                .result(false)
                .data(errResult)
                .message(errorMessage.toString())
                .error(e.getMessage())
                .build();
    }

    /**
     * 处理所有未明确捕获的异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public ResultInfo<Object> handleGeneralException(Exception e) {
        log.error(e.getMessage(), e);
        return ResultInfo.builder()
                .code(500)
                .result(false)
                .message(e.getMessage())
                .error(e.getClass().getSimpleName())
                .build();
    }

}
